/**************************************************************************************
*
*	Copyright (C) 2009 Brent Andrew Chua - See LICENSE.txt for more info.
*		
*	Filename	-	MemoryManager.h
*
*	Description -	Basic memory manager.
*
*	Comments	-	Will probably use a sequential memory management method.
*
*	Modification History:
*			Name			   Date					Description
*			CodeSushi	-	10/18/2009	-	Creation of MemoryManager
*			CodeSushi	-	11/16/2009	-	Re-writing the Memory manager for the 6th time.
**************************************************************************************/

#ifndef _MEMORYMANAGER_H_
#define _MEMORYMANAGER_H_

#define PSX_OVERRIDE_NEW_DELETE 0


#include "PulseBase.h"

namespace Pulse
{
	static class MemoryManager *gpMemoryManager;
	class MemoryBlock;

	/* Forward Declerations and Defines */
	#define PSX_MEMORY_ENTRY_HEADER_SIZE ( sizeof( MemoryEntry )  ) /* Or simply 12 if you're sure we're in X86 */
	#define PSX_MEMORY_ALIGNMENT 16
	#define PSX_BLOCK_SIZE PSX_MEGABYTE * 300 // Arbitrary value can be changed to accomodate the application's needs. 18-20 is a good starting number.
	#define PSX_MINIMUM_BLOCK_SIZE ( PSX_MEMORY_ENTRY_HEADER_SIZE + sizeof( U8*) )

	struct MemoryEntry
	{
	public:

		enum MemoryState { MEM_STATE_USED = 0, MEM_STATE_FREE = 1 };

		// We want m_pData to be implicit through this function because we only need the size
		// of the actual header of a memory entry.
		U8 * GetData( VOID );

		MemoryEntry * GetNext( VOID );

	public:

		MemoryBlock		*m_pBlockHeader;
		//MemoryEntry	*m_pNext;
		MemoryEntry		*m_pPrev;			
		U32				m_state;
		U32				m_size;			// Length in bytes
		//U8			*m_pData;		// Actual data starts here. See GetData()
	};
	
	/* MemoryBlock Definition */
	class MemoryBlock
	{
	public:
	
		MemoryBlock( U32 bytes );

		VOID * Allocate( U32 bytes );
		VOID Free( VOID *pAddr );

		~MemoryBlock( VOID );

	private:

		/* Trilogy Of Evil */
		MemoryBlock();	
		MemoryBlock( CONST MemoryBlock & );
		MemoryBlock & operator = ( CONST MemoryBlock &rhs );

		/* Helper Functions */
		MemoryEntry * Split( MemoryEntry *pCurr, U32 bytes );
		VOID Merge( MemoryEntry *pCurr, MemoryEntry *pNext );
		U8 * AlignMemoryWithHeader( U8 *pAddr, const U32 alignment, U32 *offset );

		friend class MemoryManager;

		//MemoryBlock		*m_pPrev;			// Pointer to the next Memory block
		MemoryBlock		*m_pNext;			// Pointer to the previous memory block
		MemoryEntry		*m_pNextFreeEntry;	// Next pointer ready to be allocated
		MemoryEntry		*m_pEnd;			// Sentinel end pointer
		UINT			 m_freeMem;			// This will give an estimated free size of the memory block.
		U8				*m_pData;

	};

	class MemoryManager
	{
	public:

		static MemoryManager * GetInstance( VOID );
		static VOID DeleteInstance( VOID );

		~MemoryManager( VOID );

		BOOL Initialize( VOID );
		BOOL Initialize(  U32 numbCacheBlocks, U32 cacheSize = PSX_BLOCK_SIZE );

		VOID * Allocate( U32 bytes, BOOL clear = FALSE );
		VOID Free( VOID *pAddr );

		// TODO: Needs to be implemented
		VOID * Allocate( U32 bytes, const CHAR *pFile, U32 line, BOOL clear = FALSE );
		VOID Free( VOID *pAddr , const CHAR *pFile, U32 line );

	private:

		// Trilogy Of Evil
		MemoryManager( VOID );
		MemoryManager( CONST MemoryManager & );
		MemoryManager & operator = ( CONST MemoryManager & );

		MemoryBlock *m_pPoolHead;
		MemoryBlock *m_pBestFreePool;
		static MemoryManager *m_pInstance;
	};


	VOID InitializeMemoryManager( VOID );

	VOID InitializeMemoryManager( UINT numBlocks, UINT blockSize = PSX_BLOCK_SIZE );

	VOID ShutDownMemoryManager( VOID );

};


// Override global new and delete functions
void * operator new( size_t count );
void operator delete( void* _Ptr ) throw( );

void *operator new[]( size_t _Count );
void operator delete[]( void* _Ptr ) throw( );


#endif _MEMORYMANAGER_H_